
import os
from cStringIO import StringIO
import re
import shutil
re.purge
PROJECT=None



SRC_PATH="../as/src/"
OUTPUT="bin/"
OUTPUT_SRC=OUTPUT+"js/src/"


FILE={}

GENERIC={
    "type":"[a-zA-Z0-9_\.\)\(\]\[\<\>]*",
    "all":"[a-zA-Z0-9_\.\)\(\]\[\<\>=   !\-]*",
    "no_break_line":"[^\n^\r]",
    "split":"[,=\(\) :]"
}

RULES={
    "package":"package[ \t]+([a-zA-Z0-9_\.]*)",
    "header_export":"\[JSExport\(([^)]+)\)\]",
    "header_class":"\[JSClass\(([^)]+)\)\]",
    "header_function":"\[JSFunction\(([^)]+)\)\]",
    "class":"class[ 	]+([a-zA-Z0-9_]+)([ 	]+(implement|extends?)[ 	]+([a-zA-Z0-9_]+))?([^{]*)",
    "import":"import[ \t]([a-zA-Z0-9_\.]+)\.([a-zA-Z0-9_]+)",
    "getter":"(private|public|override)?[ 	]+function[ 	]+get[ 	]+([a-zA-Z0-9_]+)\(([^)]*)\):?("+GENERIC["type"]+")",
    "setter":"(private|public|override)?[ 	]+function[ 	]+set[ 	]+([a-zA-Z0-9_]+)\(([^)]*)\):?("+GENERIC["type"]+")",
    "method":"(private|public|override)?[ 	]+function[ 	]+([a-zA-Z0-9_]+)\(([^)]*)\):?("+GENERIC["type"]+")",
    "property":"(private|public?)[ 	]+(var|const?)[ 	]+([a-zA-Z0-9_]+):("+GENERIC["type"]+")([^;]*)[;]?",
    "if":"[ 	]*if[ 	]*\("+GENERIC["all"]+"\)",
    "bracket_open":"[ 	]*{[.]*",
    "bracket_close":"[ 	]*}[.]*",
    #"text":"['|\"][^\n^\r]*['|\"]",
    "comment_line":"//[^\n^\r]*",
    "comment_block_start":"/\*",
    "comment_block_end":"\*/"
}

OTHER={
    "if":"[ 	]*if[ 	]*\("+GENERIC["all"]+"\)",
    "new":"([a-zA-Z0-9_][^;]*)new[ 	]+([a-zA-Z0-9_]"+GENERIC["type"]+")\(([^)]*)\)"
}


    #"var":"var[ 	]+([a-zA-Z0-9_]+):([a-zA-Z0-9_]+)"

 
 
 
 
class Writer():
    def __init__(self,obj):
        self.obj=obj
        self.classObj=None
        
        
    def getJSResult(self):
        #print("====")
        self.classJS=self.writeClass(self.obj.child[0])
        self.protoJS=self.writePrototype(self.obj.child[0])
        return self.classJS+self.protoJS
    
    

    
    
    def translateType(self,objectType):
        
        if objectType=="string":
            return "String"
        elif objectType.find("Vector")==0:
            return "Array"
        elif objectType.find("Dictionary")==0:
            return "Object"
        return objectType 
    
    def translateNew(self,objectType):
        objectType=self.translateType(objectType)
        if objectType=="Array":
            return "[]"
        elif objectType=="Object":
            return "{}"
        return "new "+objectType;
    
    def getDefaultValue(self,objectType):
        if objectType=="int":
            return " = 0"
        elif objectType=="uint":
            return " = 0"
        elif objectType=="Boolean":
            return " = false"
        return ""
    
    
    
    
    def addThisToMember(self,str):
        exp=re.search("(return)?([ 	]*)([a-zA-Z0-9_]*)([^\n]*)",str)
        ret=""
        for child in self.classObj.child:
            if(child.name==exp.group(3)):
                
                if exp.group(1):
                    ret+=exp.group(1)
                if exp.group(2):
                    ret+=exp.group(2)
                ret+="this."+exp.group(3)
                if exp.group(4):
                    ret+=exp.group(4)
                return ret
        
        return str
    
    def writeParam(self,childs):
        args=""
        doc=""
        for arg in childs:
            args+=arg.name+","
            if arg.valueType!="":
                doc+=("\t"*arg.parent.level)+" * @param {"+self.translateType(arg.valueType)+"}"+" "+arg.name+"\t"+arg.doc+"\n"
        return (args[:-1],doc)
    
    def writeJS(self,obj):
        ret=""
        if obj.objectType=="other":
            result=""
            if len(obj.args):
                for a in obj.args:
                    if type(a)==str:
                        result+=self.addThisToMember(a)+" "
                    elif hasattr(self,"write_"+a.objectType):
                        result+=getattr(self,"write_"+a.objectType)(a)+" "
                    else:
                        result+=a.name+" "
                ret=("\t"*obj.level)+result +";"   
            else:
                ret=("\t"*obj.level)+obj.line+";"
        return ret
    
    def writePrototype(self,obj):
        ret=""
        close=False
        readChild=False
        if obj.objectType=="package":
            readChild=True
        elif obj.objectType=="class":
            self.classObj=obj
            ret=obj.name+".prototype={\n"
            close=True
            readChild=True
           
        elif obj.objectType=="getter":
            ret+="\n"
            ret+=("\t"*obj.level)+'get '+obj.name+"(){\n"
            close=True
            readChild=True
            
        elif obj.objectType=="setter":
            arg,doc=self.writeParam(obj.args)
            ret+="\n"
            if doc!="":
                ret+=("\t"*obj.level)+"/**\n"
                ret+=doc
                ret+=("\t"*obj.level)+" */\n"
            ret+=("\t"*obj.level)+'set '+obj.name+"("+arg+"){\n"
            close=True
            readChild=True

        else:
            ret+=self.writeJS(obj)

        if readChild:
            ret2=""
            for child1 in obj.child:
                ret2+=self.writePrototype(child1)
            if obj.objectType=="class" and ret2=="":
                return ""
            ret+=ret2
            
        if obj.objectType=="getter" or obj.objectType=="setter":
            ret+=("\t"*obj.level)+"},"
        elif obj.objectType=="class":
            r=ret.rfind(",")
            if r>-1:
                ret=ret[:r]+ret[r+1:]
            ret+=("\t"*obj.level)+"}"
            
        elif close:
            ret+=("\t"*obj.level)+"}"
        
        if ret!="":
            ret+="\n"
        return ret

    
        
 

    def writeClass(self,obj):
        ret=""
        close=False
        readChild=False
        
        ##print(("\t"*obj.level)+obj.objectType)
        if obj.objectType=="package":
            readChild=True
        elif obj.objectType=="import":
            ret+='import_js("src/'+obj.path+'");'
            
        elif obj.objectType=="class":
            self.classObj=obj
            readChild=True
            arg,doc=self.writeParam(obj.args)
            ret="function "+obj.name+"("+arg+")"+"{\n\n"
            if obj.valueType and obj.valueType!="":
                r=ret
                ret='extends_js("'+obj.name+'","'+obj.valueType+'")'+"\n"
                ret+="/**"+"\n"
                ret+=" * @extends {"+obj.valueType+"}\n"
                ret+=" */"+"\n"
                ret+=r
            close=True
            
            
        elif obj.objectType=="property":
            readChild=True
            if not obj.value:
                ret=("\t"*obj.level)+"this."+obj.name+self.getDefaultValue(obj.valueType)+";"
            elif(type(obj.value)==str):
                ret=("\t"*obj.level)+"this."+obj.name+" = "+obj.value+";"
            else:
                ret=("\t"*obj.level)+"this."+obj.name+" = "+obj.value.name+";"
            #else:
            #    ret=("\t"*obj.level)+"this."+obj.name+";"
            
        elif obj.objectType=="method":
            readChild=True
            arg,doc=self.writeParam(obj.args)
            ret+="\n"
            if doc!="":
                ret+=("\t"*obj.level)+"/**\n"
                ret+=doc
                ret+=("\t"*obj.level)+" */\n"
            ret+=("\t"*obj.level)+"this."+obj.name+"=function("+arg+"){\n";
            close=True;
            
        else:
            ret+=self.writeJS(obj)
                
        if readChild:    
            for child1 in obj.child:
                ret+=self.writeClass(child1)
        if close:
            ret+=("\t"*obj.level)+"}"
        if ret!="":
            ret+="\n"
        return ret 


    def write_new(self,obj):
            return self.translateNew(obj.name)+";";
    def write_var(self,obj):
            return "var "+obj.name+";";

#############################################################
    
class ASFile(Writer):
    def __init__(self,file,output="",read=False):
        
        self.mainInfos=ObjectInfos()
        Writer.__init__(self,self.mainInfos)
        
        self.path=""
        self.export=True
        self.protoJS=""
        self.classJS=""
        self._parents=[self.mainInfos]
        self._objs={}
        self.lastObject=None

        
        self.mainInfos.name=file
        self.mainInfos.path=output
        FILE[file]=self
        self.commentBlock=None
        #self.createDir(output)
        self._file=file
        self._output=output
        self._dir="src/"
        self.level=0
        if(read):
            self.read(open(file).read())
    
    def getImportUrl(self):
        return self._output
     
    def getOutputPath(self):
        return self._output
    
    def read(self,text):
        text=text.replace("{","\n{")
        
        
        ###print(text)
        self.strIO=StringIO(text)
        self.strIO.seek(0)
        while 1:
            if not self.export:
                return
            try:
                line= self.strIO.next()
            except:
                break
            self.readLine(line)

        
    def readLine(self,line):
        line=line.replace("\t","")
        line=line.replace("\n","")
        line=line.replace("\r","")
        
        if self.commentBlock:
            ret=self.read_comment_line(line,None)
            
            self.commentBlock.args.append(ret)
            if(line.find("*/")>-1):
                self.commentBlock=None
            return ret
        
        
        for name in RULES:
            exp=re.search(RULES[name],line)
        ###print (exp, RULES[name],repr(line))
            if exp is not None:
                
                ret=getattr(self,"read_"+name)(line,exp)
                ##print "-"+name
                
                if ret:
                    return self.set_communInfo(ret)
                else:
                    return ret
        ret=self.read_other(line,None)
        if ret:
            return self.set_communInfo(ret)
        
    def set_communInfo(self,obj):

        obj.level=len(self._parents)-2
        if len(self._parents) and self._parents[-1]:
            self._parents[-1].addChild(obj)
            #print (self._parents[-1].name,self._parents[-1].block)
            #if self._parents[-1].block==False:
            #    self._parents.pop()
        if obj.value:
            obj2=self.readLine(obj.value)
            
            if obj2:
                obj2.objectType="Value"
                obj.value=obj2
        return obj
    
    
    
    def read_other(self,line,exp):
        ##print "-other"
        exp=re.search("([a-zA-Z0-9_]"+GENERIC["all"]+")",line)

        if exp is None or exp.group(0) is None or exp.group(0)=="":
            return None
        ##print("read_other",exp.groups())
        self.lastObject=ObjectInfos()
        self.lastObject.objectType="other"
        self.lastObject.name=exp.group(0)
        self.lastObject.line=line

        listSplit=re.split("("+GENERIC["split"]+")",self.lastObject.line)
        objVar=None
        objNew=None
        self.lastObject.args=[]

        

        list2=[]
        if(self._output=="ca/jsbr/jslib_as/event/EventDispatcher.js"):   
            #print(0,listSplit)
        for i in range(0,len(listSplit)):
            s=listSplit[i]
            if s=='' or s==" " or s=="    ":
                continue
            else:
                list2.append(s)
        listSplit= list2       
        if(self._output=="ca/jsbr/jslib_as/event/EventDispatcher.js"):   
            #print(1,listSplit)
        a=0
        for i in range(0,len(listSplit)):
            if a>i:
                continue
            a=i
            sub=listSplit[i]
            if sub=='':
                continue
            if re.match(GENERIC["split"],sub):
                self.last=sub
            if sub=='var':
                objVar=ObjectInfos()
                objVar.objectType="var"
                a+=1

                objVar.name=listSplit[a]
                if len(listSplit)<i+1 and listSplit[a+1]==":":
                    objVar.valueType=listSplit[a+1]
                    a+=1
                self.lastObject.args.append(objVar)
                self._parents[-1].addChild(objVar)
            #elif sub=='if':
                #self._parents.append(self.lastObject)
                #self.lastObject.args.append(sub)
            #elif sub=='for':
                #self._parents.append(self.lastObject)
                #self.lastObject.args.append(sub)
            elif sub=="new":
                objNew=ObjectInfos()
                objNew.objectType="new"
                a+=1

                objNew.name=listSplit[a]
                a+=1
                if listSplit[a]=="(":
                    objNew.arg=listSplit[a:a+listSplit[a:].index(")")]
                a=a+listSplit[a:].index(")")+1

                self.lastObject.args.append(objNew)
            else:
                self.lastObject.args.append(sub)
            #else :
        if(self._output=="ca/jsbr/jslib_as/event/EventDispatcher.js"):   
            #print(2,self.lastObject.args)
        return self.lastObject
    
    def read_if(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.type="if"
        self.lastObject.name="if"
        self._parents.append(self.lastObject)
        return self.lastObject
    
    def read_text(self,line,exp):
        return self.read_other(line,exp)

    def read_comment_block_start(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.objectType="comment"
        self.lastObject.name=line
        self.commentBlock=self.lastObject
        return self.lastObject
    
    def read_comment_block_end(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.objectType="comment"
        self.lastObject.name=line
        self.commentBlock=None
        return self.lastObject
        
    def read_comment_line(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.name=line
        self.lastObject.objectType="comment"

        return self.lastObject
        
    def read_new(self,line,exp):
        obj=ObjectInfos()
        obj.objectType="new"
        obj.name=exp.group(1)
        obj.valueType=exp.group(2)
        #obj.level=len(self._parents)-1
        if exp.group(3):
            obj.args=self.read_arg(exp.group(3))
        #if len(self._parents) and self._parents[-1]:
        #    self._parents[-1].addChild(obj)
        return obj
        ##print("new",exp.group(2))
        
    def read_package(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.name=exp.group(1)
        self.lastObject.objectType="package"
        s=exp.group(1)
        self._dir+=s.replace('.','/')
        try:
            self.lastObject.makedirs(PROJECT.output+self._dir)
        except:
            pass
        return self.lastObject
    
    def read_import(self,line,exp):
        
        obj=ObjectInfos()
        obj.objectType="import"
        obj.name=exp.group(1)+'.'+exp.group(2)
        if obj.name.find("flash.")==0:
            return None
        package=exp.group(1)+'.'+exp.group(2)
        path=exp.group(1).replace('.','/')
        if os.path.exists(PROJECT.output+path) is False:
            os.makedirs(PROJECT.output+path)
        
        asf=ASFile(SRC_PATH+path+"/"+exp.group(2)+'.as',""+path+"/"+exp.group(2)+'.js',True)
        obj.path=asf.getImportUrl()
        #asf.path="src/"+path+"/"+exp.group(2)+'.js'
        
        PROJECT.addAsFile(package,asf)
        return obj
    
    def read_header_export(self,line,exp):
        options=exp.group(1).split(",")
        for value in options:
            prop=value.split("=")
            prop[1]=prop[1][1:-1]
            PROJECT.setProperty(prop[0],prop[1][1:-1])
        return None
    
    def read_header_class(self,line,exp):
        options=exp.group(1).split(",")
        for value in options:
            prop=value.split("=")
            prop[1]=prop[1][1:-1]
            if prop[0]=="export" and prop[1]=="false":
                self.export=False
                del FILE[self._file]
            if prop[0]=="script" and prop[1]!="false":
                self._output=prop[1]
        return None
    
    def read_getter(self,line,exp):
        self.lastObject=self.read_method(line,exp)
        self.lastObject.objectType="getter"
        return self.lastObject
    
    def read_setter(self,line,exp):
        self.lastObject=self.read_method(line,exp)
        self.lastObject.objectType="setter"
        return self.lastObject
    
    def read_bracket_open(self,line,exp):
        if len(self._parents)==0 or self._parents[-1]!=self.lastObject:
            self._parents.append(self.lastObject)
        self._parents[-1].block=True
        return None
        
    
    def read_bracket_close(self,line,exp):
        self._parents.pop()
        return None
          
        
    def read_class(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.objectType="class"
        self.lastObject.name=exp.group(1)
        self.lastObject.valueType=exp.group(4)
        #self.lastObject.level=len(self._parents)-1
        self._objs["class"]=self.lastObject
        #self.mainInfos.addChild(self.lastObject)
        return self.lastObject
        
    
    def read_property(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.name=exp.group(3)
        self.lastObject.objectType="property"
        self.lastObject.valueType=exp.group(4)
        #self.lastObject.level=len(self._parents)-1
        if exp.group(5):
            self.lastObject.value=exp.group(5)
            
        #self._parents[-1].addChild(self.lastObject)
        return self.lastObject
    
    def read_arg(self,args):
        ret=[]
        splitArgs=args.split(',')
        for arg in splitArgs:
            split2=arg.split(':')
            if len(split2)>=1:
                obj=ObjectInfos()
                obj.name=split2[0]
                if len(split2)>=2:
                    obj.valueType=split2[1]
                obj.objectType="arg"
                obj.parent=self.lastObject
                ret.append(obj)
        return ret
    
    def read_method(self,line,exp):
        
        self.lastObject=ObjectInfos()
        self.lastObject.name=exp.group(2)
        self.lastObject.objectType="method"
        self.lastObject.valueType=exp.group(4)
        #self.lastObject.level=len(self._parents)-1
        self.lastObject.args=self.read_arg(exp.group(3))
        #self._parents[-1].addChild(self.lastObject)
        if self.lastObject.name==self._objs["class"].name:
            self._objs["class"].args=self.lastObject.args
        return self.lastObject
    
    def read_var(self,line,exp):
        self.lastObject=ObjectInfos()
        self.lastObject.name=exp.group(1)
        self.lastObject.objectType="var"
        self.lastObject.valueType=exp.group(2)
        #self.lastObject.level=len(self._parents)-1
        #self._parents[-1].addChild(self.lastObject)
        pass
    
#######################################################



class ObjectInfos(object):
    def __init__(self):
        self.path=""
        self.objectType=""
        self.valueType=""
        self.name=""
        self.doc=""
        self.parent=None
        self.child=[]
        self.level=0
        self.args=[]
        self.value=None
        self.line=""
        self.block=False
    
    def addChild(self,child):
        ###print self.name+"=>"+child.name+"("+child.objectType+")"
        self.child.append(child)
        child.parent=self
        
    def findParentByObjectType(self,objectType="class"):
        if self.type==objectType:
            return self
        else:
            return self.parent.findClass()
        
    def findChild(self,name):
        for child in self.child:
            if(child.name==name):
                return child
        return None
    
    def findArg(self,name):
        for arg in self.args:
            if(arg.name==name):
                return arg
        return None
    
    
    def __str__(self):
        return "ObjectInfos:"+self.name



####################################################
    
class Project(ASFile):
    def __init__(self,input,output):
        global PROJECT
        self._prop={}
        self._asfile={}
        PROJECT=self
        
        
        outputClass=input.replace(SRC_PATH,"").replace(".as",".js")
        #print outputClass
        ASFile.__init__(self,input,outputClass)
        
        self.output=output
        if os.path.exists(output):
            shutil.rmtree(output)
        os.makedirs(output)
        
        self.read(open(input).read())
        
        classJS=""
        protoJs=""
        for ind in FILE:
            #print ind
            obj=FILE[ind]
            #obj.getJSResult()
            #print 'output:'+output+obj.getOutputPath()
            open(output+obj.getOutputPath(),'w+').write(obj.getJSResult())



    def addAsFile(self,package,asfile):
        self._asfile[package]=asfile
        
        
    def getAsFile(self,package):
        return self._asfile[package]

    def setProperty(self,name,value):
        ###print(name,value)
        self._prop[name]=value
    
Project("../as/src/Main.as",OUTPUT_SRC)